import {useCallback, useEffect, useLayoutEffect, useMemo, useState} from "react";
// api
import {getStudentList} from "../../request";
import StudentItem from "./component/StudentItem";
import useRequestLoadingDispatcher from "../../hooks/useRequestLoadingDispatcher";
// 登录 登录中 loading
// 提交表单 loading

// 一旦你的属性传递超过四层 得考虑是否使用上下文
// 上下文多数情况下是用来做全局数据管理的 【vuex】 【redux: 同时redux也是基于上下文做的】
// 同时由于很多库都内置了上下文 而且这些库都很好 【router redux...】所以平时我们直接去用上下文的场景还是不多
export default function StudentList() {
  const [studentList, setStudentList] = useState([]);
  // const [studentNameListState, setStudentNameList] = useState([]);
  const {loading, executeRequest} = useRequestLoadingDispatcher();

  // useEffect(() => {
  //   console.log("studentList变化了", studentList)
  //   const _studentNameList = studentList.map(stu => stu.name);
  //   setStudentNameList(_studentNameList)
  // }, [studentList])
  // 从逻辑上讲 就是studentNameList一定会依赖于这个studentList这个数据
  //需求 就是我们只需要拿studentList里面的每个学生的name 然后会去做其他事情
  // 看起来很合理自洽
  const studentNameList = useMemo(() => studentList.map(stu => stu.name), [studentList]);// 我们项留存他的引用怎么办呢
  // 时空切片 如果你学过的Vue的话你应该知道计算属性 你就记住这个使用方式

  // const fetchStudentFromServer = async () => {
  //   executeRequest(async () => {
  //     const studentDataResponse = await getStudentList();
  //     console.log(studentDataResponse);
  //     setStudentList(studentDataResponse.data);
  //   });
  // }


  // useCallback 只在创建函数引用的时候使用
  // 1. 第一个参数 你要对应赋值给变量的函数体 【函数声明】
  // 2. 第二个参数是依赖项 当依赖项发生了变动以后 对应的函数引用会被重新生成

  // 每次函数组件重新渲染都是函数的重新执行 那么重新执行的话内部的函数上下文就会整体变化
  // 1。如果没有依赖项，fetchStudentFromServer的引用值是永恒不变的
  // 2。到了第二次渲染以后，即使studentList的值变成了[1,2,3]了，但只要fetchStudentFromSever这个函数他的引用是来自上一个时间切片【一次函数'
  // 组件的渲染所生成的上下文产生闭包，从而导致fetchStudentFromServer里如果访问了studentList,他依旧是空数组

  // 当studentList发生变化以后 会导致组件重新渲染 ---》 就会产生一个新的时间切片-> 如果某个函数要用到studentList的值，那么这个新的切片里
  // 你这个函数必须重新生成一个引用

  // 要让fetchStudentFromServer在需要刷新引用的时间切片里去刷新引用

  // 没有依赖性 在每一次组件的新的时间片里 他无所谓都直接创建新的
  const fetchStudentFromServer = useCallback(
    async () => {
      // 否则你到的还是上个时间切片的函数
      executeRequest(async () => {
        const studentDataResponse = await getStudentList();
        console.log(studentDataResponse);
        setStudentList(studentDataResponse.data);
      })

    }, [executeRequest]);
  // 记住一个真理 反正需要用到函数的地方你都用useCallback包起来
  useLayoutEffect(() => {
    // setTimeout模拟 ----》 我直接在本地搭一个服务器
    fetchStudentFromServer()
  }, [])

  // 在引入一个逻辑， 就是假设studentList为空， studentList.length === 0
  // 我们显示暂无学生数据 否则显示学生列表
  // 用一种刁钻的方式来写， 因为只有这样写才能体现useEffect 和 useLayoutEffect 的差别

  // 模拟一种情况， 让页面渲染非常多的东西， 一旦渲染多了 渲染时长必定要变长的
  // 渲染一个大型项目 -》 2s , 但是我们这个demo渲染只需要0.5s, 我们只需要想办法拖延1.5s就可以模拟大型项目了
  // for (let i = 0; i < 50000; i++) {
  //   console.log(i)
  // }
  // 现现实了暂无学生数据
  // 在显示了正在加载中
  // 最后显示了对应的学生列表

  // 每一个被循环的子元素都有一个独一无二的key值
  // 前置知识 diff算法
  return (
    <div>
      {!loading && studentList.length === 0 && <div>暂无学生数据</div>}
      {
        loading ? <div>正在加载中...</div> :
          (studentList.map(student => {
            return <StudentItem key={student.id} {...student}/>
          }))
      }
      {<div>{studentNameList}</div>}
    </div>
  )
}